import React from "react";
// 官方文档地址：https://react.docschina.org/docs/react-component.html#static-getderivedstatefromprops
//类组件 需要实例化，含有生命周期
//函数组件不需要实例化没有生命周期

//挂载阶段（初始化阶段）：constructor->getDerivedStateFromProps->render->componentDidMount
//更新阶段（运行中的阶段）：getDerivedStateFromProps->shouldComponentUpdate->render->getSnapshotBeforeUpdate->componentDidUpdate
//卸载阶段：componentWillUnmount
class SonB extends React.Component {
    state = {
        count: 0,
        name: "xiaoming",
    };

    //挂载阶段：
    constructor() {
        //创建组件时最先执行，初始化的时候只执行一次
        // 不要在这里调用 this.setState()
        //1. 初始化state  2. 创建 Ref 3. 使用 bind 解决 this 指向问题等
        //现在constructor函数作用不大了，因为上述作用1和2都能直接在constructor外部写
        super();
        // this.state = {
        //     count: 0,
        // };
        console.log("挂载阶段第一个执行的生命周期：constructor");
    }

    static getDerivedStateFromProps(nextProps, nextState) {
        //nextProps-新的属性, nextState--新的状态
        //getDerivedStateFromProps是新的生命周期函数，使用它组件必须要有状态，必须要加static（因为它是类的一个静态方法），必须要return出一个对象
        // getDerivedStateFromProps 会在调用 render 方法之前调用，并且在初始挂载及后续更新时都会被调用。
        //它应返回一个对象来更新 state，如果返回 null 则不更新任何内容，返回的对象中的属性会与原状态里的属性进行合并更新而不是完全取代原state状态。
        console.log(
            "挂载阶段第二个执行或更新阶段第一个执行的生命周期：getDerivedStateFromProps"
        );
        //console.log(this); //undefined static静态的，this为undefined
        return {
            count: nextState.count + "---gdsfp处理后", //初始化时和更新时提前对state状态里的count属性进行修改，但不影响state.name属性
        };
        //此生命周期常常只用于根据新的props来更新状态，而不进行其它的操作
        //此生命周期在一定情况下配合componentDidMount和componentDidUpdate可替代componentWillMount和componentWiiReceiveProps
    }

    // componentWillMount() {
    //     组件即将挂载之前，在constructor之后render之前
    //     React18已经弃用，要想使用可使用UNSAFE_componentWillMount（不推荐使用）
    //     同样的还有：componentWillUpdate（更新阶段的，dom更新前执行，render前shouldComponentUpdate后执行）
    //                componentWiiReceiveProps (更新阶段的，父组件修改属性或状态时触发，即在子组件使用才有意义，shouldComponentUpdate前执行)
    // }
    // UNSAFE_componentWillMount() {
    //     console.log("即将弃用的生命周期");
    // }

    render() {
        //每次组件渲染时都会触发，作用渲染UI
        //注意不能在这里执行setState()，不然会造成死循环
        //因为state状态的改变会触发render的重新执行
        console.log(
            "挂载阶段第三个执行的生命周期或更新阶段第三个执行的生命周期：render"
        );
        return (
            <div>
                <button
                    onClick={() => {
                        this.setState({
                            count: new Date(),
                        });
                    }}>
                    修改state.count状态
                </button>
                <button
                    onClick={() => {
                        this.setState({
                            count: this.state.count,
                        });
                    }}>
                    调用setState但更新的状态与老状态相同
                </button>
                {this.state.count}---
                {/* 会展示getDerivedStateFromProps修改过的状态 */}
                {this.state.name}
            </div>
        );
    }

    componentDidMount() {
        //组件挂载（完成DOM渲染）后执行，初始化的时候只执行一次
        //ajax请求，Dom操作，订阅函数调用，启用定时器

        //如果数据是组件的状态需要去影响视图，定义到state中
        //而如果我们需要的数据不和视图绑定，定义成一个普通的实例属性即可，如timer
        //state中尽量保持精简
        this.timer = setInterval(() => {
            console.log("定时器开始");
        }, 1000);
        console.log("挂载阶段第四个执行的生命周期：componentDidMount");
    }

    // 更新阶段
    // 更新阶段第一个执行的生命周期：getDerivedStateFromProps

    shouldComponentUpdate(nextProps, nextState) {
        //当 props 或 state 发生变化时，shouldComponentUpdate() 会在渲染执行之前被调用。返回值默认为 true
        //返回false时会阻止render函数的执行，即阻止渲染
        //nextProps, nextState分别为新的props和新的状态（老状态通过this.state取）
        console.log("更新阶段第二个执行的生命周期：shouldComponentUpdate");
        // console.log(nextProps, nextState);
        //判断数组或者对象是否相等可将其转化成json字符串进行比较
        if (JSON.stringify(this.state) === JSON.stringify(nextState)) {
            //新老状态完全相同时阻止重新渲染，起到一定作用的性能优化
            return false;
        }
        return true;
    }

    //更新阶段第三个执行的生命周期：render

    getSnapshotBeforeUpdate() {
        console.log("更新阶段第四个执行的生命周期：getSnapshotBeforeUpdate");
        // getSnapshotBeforeUpdate() 在最近一次渲染输出（提交到 DOM 节点）之前调用，render之后，dom渲染之前。
        //它使得组件能在发生更改之前从 DOM 中捕获一些信息（例如，滚动位置）。此生命周期的任何返回值将作为参数传递给 componentDidUpdate()的第三个参数。
        return null;
        //它取代了componentWillUpdate
    }

    componentDidUpdate(prevProps, prevState, snapshot) {
        // prevProps--老的属性, prevState--老的状态,snapshot--getSnapshotBeforeUpdate的返回值
        //使用场景不是很多
        //组件更新后（DOM更新渲染完毕），可以获取到更新后的DOM内容
        //不要直接调用setState
        console.log("更新阶段第五个执行的生命周期：componentDidUpdate");
    }

    //卸载阶段
    componentWillUnmount() {
        //组件卸载执行
        //执行清理工作
        clearInterval(this.timer);
        console.log("卸载阶段执行的生命周期：componentWillUnmount");
    }
}

//父组件App
class App extends React.Component {
    state = {
        flag: true,
    };
    setFlag = () => {
        this.setState({
            flag: !this.state.flag,
        });
    };
    render() {
        return (
            <div className='App'>
                <button onClick={this.setFlag}>展示/隐藏SonB组件</button>
                <hr />
                {this.state.flag ? <SonB></SonB> : null}
            </div>
        );
    }
}

export default App;
